/*
 * Copyright (c) 2018-2024 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

#include <config.h>
#include <rpc/types.h>
#include <netdb.h>
#include <sys/socket.h>

#if defined(HAVE_TCL8_6_TCL_H)
#include <tcl8.6/tcl.h>
#elif defined(HAVE_TCL8_5_TCL_H)
#include <tcl8.5/tcl.h>
#elif defined(HAVE_TCL_TCL_H)
#include <tcl/tcl.h>
#elif defined(HAVE_TCL_H)
#include <tcl.h>
#endif

#include "common.h"
#include "tclinfo.h"

static struct Tcl_Node *sptrTclNode;
static struct Tcl_Interp *globinterp;
static int sintOverRide;
static struct Tcl_Resource *sptrTclRes;
/* Arrays holding symbols used in resource requirement
 * expressions.
 */
static int *sptrNumericIndicies;
static int *sptrBooleanIndicies;
static int *sptrStringIndicies;

static int numeric_value (ClientData, Tcl_Interp *, Tcl_Value *, Tcl_Value *);
static int bool_value (ClientData, Tcl_Interp *, Tcl_Value *, Tcl_Value *);
static int string_value (ClientData, Tcl_Interp *, int, const char **);
static char *get_value(unsigned int);

/* numeric_value()
 * Evaluate host or shared resource numerica value.
 */
static int
numeric_value(ClientData clientData, Tcl_Interp *interp, Tcl_Value *args, Tcl_Value *result)
{
    unsigned int *indx;
    indx = (unsigned int *)clientData;

    result->type = TCL_INT;

    if (*indx < sptrTclRes->num_index) {

        result->type = TCL_DOUBLE;
        if (*indx <= R15M) {
            result->doubleValue = sptrTclNode->load[*indx] - 1;
        } else {
            result->doubleValue = sptrTclNode->load[*indx];
            if (sptrTclNode->load[*indx] >= (INFINIT_DOUBLE - 10.0)
                && TCL_CHECK_SYNTAX != sptrTclNode->check_flag) {

                return (TCL_ERROR);
            }
        }

        return TCL_OK;

    }

    if (*indx == MAXCPUS) {
        result->intValue = sptrTclNode->max_cpu;
    } else if (*indx == MAXMEM) {
        result->doubleValue = sptrTclNode->max_mem;
    } else if (*indx == MAXSWAP) {
        result->doubleValue = sptrTclNode->max_swp;
    } else if (*indx == MAXTMP) {
        result->doubleValue = sptrTclNode->max_tmp;
    } else {

        char *value;
        value = get_value(*indx);
        if (value == NULL || !strcmp(value,"-")) {
            result->intValue = 0;
            return(TCL_OK);
       }

        result->doubleValue = atof(value);
        result->type = TCL_DOUBLE;
    }

    return TCL_OK;
}

/* bool_value()
 * Evaluate host based on shared resource bool.
 */
static int
bool_value(ClientData clientData, Tcl_Interp *interp, Tcl_Value *args, Tcl_Value *result)
{
    unsigned int *indx;
    indx = (unsigned int *)clientData;

    if (*indx < 0) {
        return(TCL_ERROR);
    }

    sintOverRide = TRUE;

    result->type = TCL_INT;

    if (*indx == SERVER) {
        result->intValue = (sptrTclNode->server == 1 || sptrTclNode->server == 2);
        return TCL_OK;
    }

    /* Is a host based resource.
     */
    int isSet;
    TEST_BIT(*indx, sptrTclNode->res_bitmap, isSet);
    if (isSet == 1) {
        result->intValue = isSet;
        return TCL_OK;
    }

    char *value;
    value = get_value(*indx);
    if (value == NULL || value[0] == '-') {

        if (TCL_CHECK_SYNTAX == sptrTclNode->check_flag) {
            result->intValue = 1;
        } else {
            result->intValue = 0;
        }
    } else {
        result->intValue = atoi(value);
    }

    return TCL_OK;
}

/* string_value()
 * Evaluate host absed on shared resource string value.
 */
static int
string_value(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
{
    unsigned int *indx;
    char *sp;
    char *sp2;
    char *value;
    char status[20];
    struct hostent *hp;

    if (argc != 3) {
        Tcl_SetResult(interp, (char *)"wrong # args", NULL);
        return TCL_ERROR;
    }

    indx = (unsigned int *)clientData;

    if (*indx < 0) {
        return(TCL_ERROR);
    }

    if (*indx == HOSTNAME) {
        sintOverRide = TRUE;
        sp = sptrTclNode->host_name;
        hp = gethostbyname((char *)argv[2]);
        if (hp)
            sp2 = hp->h_name;
        else
            sp2 = (char *)argv[2];
    } else if (*indx == HOSTTYPE) {
        sp = sptrTclNode->host_type;

        if (strcmp(argv[2], LOCAL_STR) == 0) {
            sp2 = sptrTclNode->from_type;
            if (strcmp(argv[1], "eq") != 0)
                sintOverRide = TRUE;
        } else {
            sintOverRide = TRUE;
            sp2 = (char *)argv[2];
        }
    } else if (*indx == HOSTMODEL) {
        sintOverRide = TRUE;
        sp = sptrTclNode->host_model;

        if (strcmp(argv[2], LOCAL_STR) == 0) {
            sp2 = sptrTclNode->from_model;
        } else {
            sp2 = (char *)argv[2];
        }
    } else if (*indx == HOSTSTATUS) {
        status[0] = '\0';
        sintOverRide = TRUE;
        if (STATUS_ISUNAVAIL(sptrTclNode->status)) {
            strcpy(status, "unavail");
        } else if (STATUS_ISBUSY(sptrTclNode->status)) {
            strcpy(status, "busy");
        } else if (STATUS_ISCLOSEWIN(sptrTclNode->status)) {
            strcpy(status, "closewin");
        } else {
            strcpy(status, "ok");
        }
        sp = status;
        sp2 = (char *)argv[2];
    } else {

        value = get_value(*indx);
        if (value == NULL || value[0] == '-') {
            if (TCL_CHECK_SYNTAX == sptrTclNode->check_flag) {
                Tcl_SetResult(interp, (char *)"1", NULL);
                return(TCL_OK);
            } else {
                return (TCL_ERROR);
            }
        }
        sintOverRide = TRUE;
        sp = value;
        sp2 = (char *)argv[2];
    }

    if (strcmp(sp2, WILDCARD_STR) == 0 ) {
        Tcl_SetResult(interp, (char *)"1", NULL);
        return TCL_OK;
    }

    if (strcmp(argv[1],"eq") == 0) {
        if (strcmp(sp2, sp) == 0)
            Tcl_SetResult(interp, (char *)"1", NULL);
        else
            Tcl_SetResult(interp, (char *)"0", NULL);
    } else if (strcmp(argv[1],"ne") == 0) {
        if (strcmp(sp2, sp) != 0)
            Tcl_SetResult(interp, (char *)"1", NULL);
        else
            Tcl_SetResult(interp, (char *)"0", NULL);
    } else if (strcmp(argv[1],"ge") == 0) {
        if (strcmp(sp2, sp) <= 0)
            Tcl_SetResult(interp, (char *)"1", NULL);
        else
            Tcl_SetResult(interp, (char *)"0", NULL);
    } else if (strcmp(argv[1],"le") == 0) {
        if (strcmp(sp2,sp) >= 0)
            Tcl_SetResult(interp, (char *)"1", NULL);
        else
            Tcl_SetResult(interp, (char *)"0", NULL);
    } else if (strcmp(argv[1],"gt") == 0) {
        if (strcmp(sp2, sp) < 0)
            Tcl_SetResult(interp, (char *)"1", NULL);
        else
            Tcl_SetResult(interp, (char *)"0", NULL);
    } else if (strcmp(argv[1],"lt") == 0) {
        if (strcmp(sp2, sp) > 0)
            Tcl_SetResult(interp, (char *)"1", NULL);
        else
            Tcl_SetResult(interp, (char *)"0", NULL);
    } else {
        return TCL_ERROR;
    }

    return TCL_OK;
}

/* Initialize the tcl interpreter for the evaluation
 * of resource requirement expressions.
 */
int
initialize_tcl(unsigned int numidx, unsigned int numuidx, unsigned int numres, unsigned int boolidx,
               unsigned int stringidx, struct resource_definition *restable)
{
    if (NULL != sptrTclRes) {
        FREEUP(sptrTclRes);
    }

    if (NULL != globinterp) {
        Tcl_DeleteInterp(globinterp);
        globinterp = NULL;
    }

    sptrTclRes = (struct Tcl_Resource *)calloc(1, sizeof(struct Tcl_Resource));
    if (NULL == sptrTclRes) {
        return -1;
    }

    sptrTclRes->num_index = numidx;
    sptrTclRes->num_user_index = numuidx;
    sptrTclRes->bool_start_index = boolidx;
    sptrTclRes->string_start_index = stringidx;
    sptrTclRes->num_resource = numres;
    sptrTclRes->resources = restable;

    globinterp = Tcl_CreateInterp();

    /* The math functions are invoked by the interpreter
     * while evaluating the expression. The expression itself
     * is made of symbols R15S, R1M etc which we define
     * her as functions together with input (ClientData)
     * that tcl has to pass them.
     */
    sptrNumericIndicies = (int *)calloc(sptrTclRes->num_resource, sizeof(int));
    if (NULL == sptrNumericIndicies) {
        return -1;
    }

    unsigned int i, resIdx;
    i = 0;
    for (resIdx=0; resIdx<sptrTclRes->num_resource; resIdx++) {

        if (TYPE_NUMERIC != sptrTclRes->resources[resIdx].value_type) {
            continue;
        }

        sptrNumericIndicies[i] = resIdx;
        Tcl_CreateMathFunc(globinterp, sptrTclRes->resources[resIdx].name, 0, NULL,
                           numeric_value, (ClientData)&(sptrNumericIndicies[i]));
        i ++;
    }

    sptrBooleanIndicies = (int *)calloc(sptrTclRes->num_resource, sizeof(int));
    if (NULL == sptrBooleanIndicies) {
        return -1;
    }

    i = 0;
    for (resIdx=0; resIdx<sptrTclRes->num_resource; resIdx++) {

        if (TYPE_BOOLEAN != sptrTclRes->resources[resIdx].value_type) {
            continue;
        }

        sptrBooleanIndicies[i] = resIdx;
        Tcl_CreateMathFunc(globinterp, sptrTclRes->resources[resIdx].name, 0, NULL,
                           bool_value, (ClientData)&(sptrBooleanIndicies[i]));
        i ++;
    }

    sptrStringIndicies = (int *)calloc(sptrTclRes->num_resource, sizeof(int));
    if (NULL == sptrStringIndicies) {
        return -1;
    }

    i = 0;
    for (resIdx=0; resIdx<sptrTclRes->num_resource; resIdx++) {

        if (TYPE_STRING != sptrTclRes->resources[resIdx].value_type) {
            continue;
        }

        sptrStringIndicies[i] = resIdx;
        Tcl_CreateCommand(globinterp, sptrTclRes->resources[resIdx].name, string_value,
                          (ClientData)&(sptrStringIndicies[i]), NULL);
        i ++;
    }

    return 0;
}

void
finalize_tcl(void)
{
    if (NULL != sptrTclRes) {
        FREEUP(sptrTclRes);
    }

    if (globinterp) {
        Tcl_DeleteInterp(globinterp);
        globinterp = NULL;
    }

    return;
}

int
evaluate(char *strreq, struct Tcl_Node *tclnode, char useFromType)
{
    int code;
    unsigned int i;
    int resBits;

    sptrTclNode = tclnode;

    sintOverRide = FALSE;
    sptrTclNode->override = sintOverRide;

    code = Tcl_Eval(globinterp, strreq);
    if (code != TCL_OK) {
        return -1;
    }

    resBits = 0;
    if (!sptrTclNode->ignDedicatedResource && NULL != sptrTclNode->exclres_bitmap) {
        for (i = 0; i < GET_INTNUM(sptrTclRes->num_resource); i++) {
            resBits += sptrTclNode->exclres_bitmap[i];
        }

        if (resBits != 0 && sptrTclNode->res_bitmap) {

            resBits = 0;
            for (i = 0; i < GET_INTNUM(sptrTclRes->num_resource); i++) {
                resBits += sptrTclNode->res_bitmap[i] & sptrTclNode->exclres_bitmap[i];
            }

            if (resBits == 0) {
                return 0;
            }
        }
    }

    if (!sintOverRide && useFromType) {
        if (strcmp(sptrTclNode->host_type, sptrTclNode->from_type) != 0) {
            return 0;
        }
    }

    if (strcmp(Tcl_GetStringResult(globinterp), "0") == 0) {
        return 0;
    }

    return 1;
}

static char *
get_value(unsigned int index)
{
    if (index > sptrTclRes->num_resource || HASH_NUM_ENTS(sptrTclNode->respair_table) == 0) {
        return NULL;
    }

    char *value;
    value = (char *)gf_hash_find(sptrTclNode->respair_table, sptrTclRes->resources[index].name);

    return (value);
}

/* freeTclInterp()
 */
void
freeTclInterp(void)
{
    if (globinterp == NULL) {
        return;
    }

    Tcl_DeleteInterp(globinterp);

    return;
}

void
free_tclnode(struct Tcl_Node *tclnode)
{
    if (NULL == tclnode) {
        return;
    }

    gf_hash_free(tclnode->respair_table, free);
    FREEUP(tclnode->load);

    return;
}

